/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "../registry.h"
#include <win32/win32.h>

namespace liba
{
namespace win32
{
namespace registry
{


static bool string_starts_with(const std::string &str, const std::string & pattern)
{
	if(str.size() < pattern.size())
		return false;

	return str.substr(0, pattern.size()) == pattern;
}

static const std::string parse_base_key(const std::string & key, HKEY * base_key)
{
	const std::string base_classes	("HKEY_CLASSES_ROOT");
	const std::string base_user		("HKEY_CURRENT_USER");
	const std::string base_config	("HKEY_CURRENT_CONFIG");
	const std::string base_machine	("HKEY_LOCAL_MACHINE");
	const std::string base_users		("HKEY_USERS");

	if(string_starts_with(key, base_classes))
	{
		*base_key = HKEY_CLASSES_ROOT;
		return key.substr(base_classes.size() + 1);
	}
	if(string_starts_with(key, base_config))
	{
		*base_key = HKEY_CURRENT_CONFIG;
		return key.substr(base_config.size() + 1);
	}
	if(string_starts_with(key, base_machine))
	{
		*base_key = HKEY_LOCAL_MACHINE;
		return key.substr(base_machine.size() + 1);
	}
	if(string_starts_with(key, base_users))
	{
		*base_key = HKEY_USERS;
		return key.substr(base_users.size() + 1);
	}
	if(string_starts_with(key, base_user))
	{
		*base_key = HKEY_CURRENT_USER;
		return key.substr(base_user.size() + 1);
	}
	return "";
}

bool write(const std::string & key, const std::string & value, const std::string & data)
{
	HKEY base_key;
	std::string subkey_str = parse_base_key(key, &base_key);

	HKEY sub_key;
	DWORD ignored;
	if(ERROR_SUCCESS != RegCreateKeyEx(base_key, subkey_str.c_str(), 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &sub_key, &ignored))
		return false;

	size_t temp = data.size() + 1;
	unsigned int temp2 = temp;
	DWORD hren = temp2;

	if(ERROR_SUCCESS != RegSetValueEx(sub_key,
		value.c_str(), 0,
		REG_SZ,
		reinterpret_cast<const BYTE*>(data.c_str()),
		temp))
	{
		RegCloseKey(sub_key);
		return false;
	}

	RegCloseKey(sub_key);

	return true;
}

bool read(const std::string & key, const std::string & value, std::string * data)
{
	HKEY base_key;
	std::string subkey_str = parse_base_key(key, &base_key);

	HKEY sub_key;
	DWORD ignored;
	if(ERROR_SUCCESS != RegCreateKeyEx(base_key, subkey_str.c_str(), 0, 0, REG_OPTION_NON_VOLATILE, KEY_READ, 0, &sub_key, &ignored))
		return false;

	unsigned char not_used;
	unsigned long buf_size = 1;
	unsigned long type;
	if(ERROR_MORE_DATA != RegQueryValueEx(sub_key, value.c_str() , 0, &type, &not_used, &buf_size))
		return false;

	if(type != REG_SZ)
		return false;

	//Of course, the String::lock() method may be used here.
	//But that won't work for String :(.
	//Anyway, operations with windows registry are not so critical :).
	char * buf = new char[buf_size + 1];
	if(ERROR_SUCCESS != RegQueryValueEx(sub_key, value.c_str(), 0, &type, reinterpret_cast<BYTE*>(buf), &buf_size))
	{
		RegCloseKey(sub_key);
		delete[]buf;
		return false;
	}

	RegCloseKey(sub_key);
	*data = buf;
	delete[]buf;

	return true;
}

bool write(const std::string & key, const std::string & value, long number)
{
	HKEY base_key;
	std::string subkey_str = parse_base_key(key, &base_key);

	HKEY sub_key;
	DWORD ignored;
	if(ERROR_SUCCESS != RegCreateKeyEx(base_key, subkey_str.c_str(), 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &sub_key, &ignored))
		return false;

	if(ERROR_SUCCESS != RegSetValueEx(sub_key, value.c_str(), 0, REG_DWORD, reinterpret_cast<const BYTE*>(&number), sizeof(DWORD)))
	{
		RegCloseKey(sub_key);
		return false;
	}

	RegCloseKey(sub_key);

	return true;
}

bool read(const std::string & key, const std::string & value, long * number)
{
	HKEY base_key;
	std::string subkey_str = parse_base_key(key, &base_key);

	HKEY sub_key;
	DWORD ignored;
	if(ERROR_SUCCESS != RegCreateKeyEx(base_key, subkey_str.c_str(), 0, 0, REG_OPTION_NON_VOLATILE, KEY_READ, 0, &sub_key, &ignored))
		return false;

	unsigned char not_used;
	unsigned long buf_size = 1;
	unsigned long type;
	if(ERROR_MORE_DATA != RegQueryValueEx(sub_key, value.c_str() , 0, &type, &not_used, &buf_size))
		return false;

	if(type != REG_DWORD)
		return false;

	if(ERROR_SUCCESS != RegQueryValueEx(sub_key, value.c_str(), 0, &type, reinterpret_cast<BYTE*>(number), &buf_size))
	{
		RegCloseKey(sub_key);
		return false;
	}

	RegCloseKey(sub_key);
	//data->assign(buf);
	//delete[]buf;


	return true;
}

bool delete_key(const std::string & key)
{
	HKEY base_key;
	std::string subkey_str = parse_base_key(key, &base_key);

	if(ERROR_SUCCESS != RegDeleteKey(base_key, subkey_str.c_str()))
		return false;
	
	return true;
}

bool delete_value(const std::string & key, const std::string & value)
{
	HKEY base_key;
	std::string subkey_str = parse_base_key(key, &base_key);

	HKEY the_key;
	if(ERROR_SUCCESS != RegOpenKeyEx(base_key, subkey_str.c_str(), 0, KEY_SET_VALUE, &the_key))
		return true;	//the key may not exist

	bool succeeded = (ERROR_SUCCESS == RegDeleteValue(the_key, value.c_str()));

	RegCloseKey(the_key);
	return succeeded;
}

} // namespace registry
} // namespace win32
} // namespace liba

